// 定义一个全局变量
let _Vue = null
// 记录是否是hash模式
let isHash = true
export default class VueRouter {
  static install (Vue) {
    // 1.判断当前插件是否已被安装
    // 给install方法增加一个installed属性
    if (VueRouter.install.installed) {
      return
    }
    // 记录插件被安装
    VueRouter.install.installed = true
    // 2.把Vue构造函数记录到全局变量(我们要在VueRouter的实例方法中类使用Vue的构造函数，比如创建组件的时候使用Vue.component)
    _Vue = Vue

    // 混入
    _Vue.mixin({
      beforeCreate () {
        if (this.$options.router) {
          // 3.把创建Vue实例时候传入的router对象注入到所有Vue实例上
          _Vue.prototype.$router = this.$options.router// 获取到Vue实例才能写
          // 当插件注册完成的时候调用初始化方法
          this.$options.router.init()
        }
      }
    })
  }
  // 初始化三个属性options，routeMap，data属性

  constructor (options) {
    // 记录传入的options
    this.options = options
    // 记录当前的路由模式
    isHash = options.mode == "hash"
    let defaultPath = "/"
    if(isHash){
      defaultPath = window.location.hash.replace("#","")||"/"
    }else{
      defaultPath = window.location.pathname
    }
    // 解析路由规则存储到routeMap里面
    // router-view这个组件会根据当前地址寻找routeMap中对应的组件，渲染到浏览器中
    this.routeMap = {}
    this.data = _Vue.observable({
      // 存储当前的路由地址，当路由变化时要自动加载组件
      // data要设置成响应式的对象，observable用来创建响应式对象，可以直接用着渲染函数或者计算属性里面
      current: defaultPath
    })
  }

  init () {
    this.createRouterMap()
    this.initComponents(_Vue)
    this.initEvent()
  }

  createRouterMap () {
    // 遍历所有路由规则，然后解析成键值对的形式存储的routeMap这个对象里面来
    // 所有的路由规则都在options这个选项中，构造函数中传入了一个options，里面的某个属性叫routes
    // routes 这个数组存储了所有的路由规则，在构造函数中我们把options这个选项存储到了当前VueRouter的options属性里面
    // 当把routes 所有内容解析出来放到routerMap里面
    this.options.routes.forEach(route => {
      this.routeMap[route.path] = route.component
    })
  }

  initComponents (Vue) { // 传入Vue而不传入_Vue是为了减少该方法和外部的依赖
    Vue.component('router-link', {
      // 接受外部传入的参数
      props: {
        to: String
      },
      // 使用运行时版本的vue也就是不带编译器，此时不支持template选项
      // 编译器的作用只是把template转换为render函数
      // template:'<a :href = "to><slot></slot></a>'
      // 在运行时版本中直接写render函数
      render (h) {
        return h('a', {
          attrs: {
            href: this.to
          },
          on: {
            click: this.clickHandler// 给click注册clickHandler的点击事件
          }
        }, [this.$slots.default])
        // 要创建的元素对应的选择器;设置a标签属性;通过代码(获取默认插槽：this.$slots.default)的形式获取slot内容放到数组当中来。
      },
      methods: {
        clickHandler (e) { // 传入事件参数e
          if(isHash){
            window.location.hash = this.to
          }else{
            // 调用pushstate方法改变浏览器的地址栏，不会向服务器发送请求
          history.pushState({}, '', this.to)
          this.$router.data.current = this.to
          e.preventDefault()// 阻止e的默认行为
          }
          
        }
      }
    //   template: '<a :href = "to"><slot></slot></a>'
    })

    // 定义变量存储this，initcomponent中的this存储到self里面，self就是VueRouter的实例
    const self = this
    Vue.component('router-view', {
      render (h) {
        // 先找到当前路由的地址，再根据当前路由的地址去routermap对象中来找路由地址对应的组件
        const component = self.routeMap[self.data.current]
        return h(component)
      }
    })
  }

  initEvent () { // 注册popstate事件
    window.addEventListener('popstate', () => {
      // 把当前地址栏的地址取出来，把路径部分取出放到data.current里
      this.data.current = window.location.pathname
    })
    // 监听URL片段标识符变化
    window.addEventListener('hashchange', () => {
      this.data.current = window.location.hash.replace("#","")
    })
  }
}
